using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Microsoft.Xna.Framework;

namespace Oops.Xna.Framework
{
	/// <summary>
	/// Provides the abstract base class for a collection of <see cref="Microsoft.Xna.Framework.IGameComponent"/> instances whose keys are embedded in the values.
	/// </summary>
	/// <typeparam name="TKey">The type of keys in the collection.</typeparam>
	/// <typeparam name="TValue">The type of items in the collection.</typeparam>
	public abstract class KeyedGameComponentCollection<TKey, TValue> : KeyedCollection<TKey, TValue> where TValue : IGameComponent
	{
		/// <summary>
		/// Raised when a component is added to the <see cref="KeyedGameComponentCollection&lt;TKey, TValue&gt;"/>.
		/// </summary>
		public event EventHandler<GameComponentCollectionEventArgs<TValue>> ComponentAdded;

		/// <summary>
		/// Raised when a component is removed from the <see cref="KeyedGameComponentCollection&lt;TKey, TValue&gt;"/>.
		/// </summary>
		public event EventHandler<GameComponentCollectionEventArgs<TValue>> ComponentRemoved;

		private GameComponentCollectionEventArgs<TValue> eventArgs = new GameComponentCollectionEventArgs<TValue>();

		protected override void InsertItem(int index, TValue item)
		{
			// The item being added must not be in the collection already.
			if (base.IndexOf(item) != -1)
			{
				throw new ArgumentException("Resources.CannotAddSameComponentMultipleTimes");
			}

			// Call inherited method.
			base.InsertItem(index, item);

			// Notify subscribers of added component.
			if (item != null)
			{
				this.eventArgs.Item = item;
				this.OnComponentAdded(this.eventArgs);
			}
		}

		protected override void RemoveItem(int index)
		{
			// Get the item at the specified index.
			TValue item = base[index];

			// Call inherited method.
			base.RemoveItem(index);

			// Notify subscribers of removed component.
			if (item != null)
			{
				this.eventArgs.Item = item;
				this.OnComponentRemoved(this.eventArgs);
			}
		}

		protected override void SetItem(int index, TValue item)
		{
			// Setting items is not supported.
			throw new NotSupportedException("Resources.CannotSetItemsIntoGameComponentCollection");
		}

		protected override void ClearItems()
		{
			// Notify subscribers of removed components.
			for (int i = 0; i < this.Count; i++)
			{
				this.eventArgs.Item = this.Items[i];
				this.OnComponentRemoved(this.eventArgs);
			}

			// Call inherited method.
			base.ClearItems();
		}

		protected virtual void OnComponentAdded(GameComponentCollectionEventArgs<TValue> e)
		{
			// Notify subscribers of added component.
			if (this.ComponentAdded != null)
			{
				this.ComponentAdded(this, e);
			}
		}

		protected virtual void OnComponentRemoved(GameComponentCollectionEventArgs<TValue> e)
		{
			// Notify subscribers of removed component.
			if (this.ComponentRemoved != null)
			{
				this.ComponentRemoved(this, e);
			}
		}
	}
}
